'''
Copyright 2011 Eugeniu Costetchi

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

Created on Jan 20, 2011

@author: Eugeniu Costetchi
'''
from networkx.classes.digraph import DiGraph
from networkx.algorithms.operators import compose
from DialogueManager.planning.Action import BeginPointer, EndPointer, \
    Action, EndIfControl, IfControl, EndLoopControl, WhileControl, \
    ForEachControl, StartPointer, StopPointer, SayInteractionAction,\
    PlanControls
from DialogueManager.planning import visualizer
from DialogueManager.planning.Operator import ValueOfVariable

class PlanFragment(object):
    '''
    represents a plan fragment, which by
    analogy with java represents a code block between accolades { }
    it is any sequence of actions that is delimited by begin and end pointers 
    '''
    def __init__(self):
        '''
        Constructor
        '''
        self.graph = DiGraph()
        self.begin_node = BeginPointer()
        self.end_node = EndPointer()
        self.construction_pointer = self.begin_node
        self.graph.add_edge(self.construction_pointer, self.end_node)
    
    def add_fragment(self, fragment):
        ''' adds a fragment to the current construction pointer'''
        self.graph.remove_edge(self.construction_pointer, self.end_node)
        self.graph = compose(self.graph, fragment.graph)
        self.graph.add_edge(self.construction_pointer, fragment.begin_node)
        
        self.graph.add_edge(fragment.end_node, self.end_node)
        self.construction_pointer = fragment.end_node
        return self
    
    def __add__(self, other):
        if isinstance(other, Action):
            return self.add_action(other)
        elif isinstance(other, PlanFragment):
            return self.add_fragment(other)
        elif isinstance(other, list):
            return self.add_sequence(other)
        else: 
            return None
        
    def add_sequence(self, action_list):
        ''' '''
        self.graph.remove_edge(self.construction_pointer, self.end_node)
        
        self.graph.add_path(action_list)
        self.graph.add_edge(self.construction_pointer, action_list[0])
        self.construction_pointer = action_list[-1]
        
        self.graph.add_edge(self.construction_pointer, self.end_node)
        return self
    
    def add_action(self, action):
        ''' '''
        self.graph.remove_edge(self.construction_pointer, self.end_node)
        
        self.graph.add_edge(self.construction_pointer, action)
        self.construction_pointer = action
        
        self.graph.add_edge(self.construction_pointer, self.end_node)
        return self
    
    def add_condition(self, condition, then_fragment, else_fragment=None):
        ''' '''
        self.graph = compose(self.graph, then_fragment.graph)
        aux_else = None
        if else_fragment is not None:
            aux_else = else_fragment.begin_node
            self.graph = compose(self.graph, else_fragment.graph)
        
        end_if = EndIfControl()
        if_control = IfControl(condition_operator=condition,
                               end_if=end_if,
                               then_next_action=then_fragment.begin_node,
                               else_next_action=aux_else)
        end_if.if_control = if_control
        
        self.add_sequence([if_control, end_if])
        self.graph.add_edge(if_control, then_fragment.begin_node)
        self.graph.add_edge(then_fragment.end_node, end_if)
        if aux_else is not None:
            self.graph.add_edge(if_control, else_fragment.begin_node)
            self.graph.add_edge(else_fragment.end_node, end_if)
        return self
        
    def add_while(self, condition, blok_fragment):
        ''' '''
        end_while = EndLoopControl()
        while_control = WhileControl(condition, end_while, blok_fragment.begin_node)
        end_while.loop_control = while_control
        
        self.graph = compose(self.graph, blok_fragment.graph)
        self.add_sequence([while_control, end_while])
        self.graph.add_edge(while_control, blok_fragment.begin_node)
        self.graph.add_edge(blok_fragment.end_node, end_while)
        
        return self
    
    def add_for_each(self, iterator_ctx_variable, iterable_list_ctx_variable, blok_fragment):
        ''' '''
        end_for = EndLoopControl()
        for_control = ForEachControl(iterator_ctx_variable,
                                     iterable_list_ctx_variable,
                                     end_for,
                                     blok_fragment.begin_node)
        end_for.loop_control = for_control
        
        self.graph = compose(self.graph, blok_fragment.graph)
        self.add_sequence([for_control, end_for])
        self.graph.add_edge(for_control, blok_fragment.begin_node)
        self.graph.add_edge(blok_fragment.end_node, end_for)
        
        return self
    
class Plan(PlanFragment):
    ''' there is only one plan which encloses the plan fragments.
     the only difference are the start/stop markers'''
    def __init__(self):
        PlanFragment.__init__(self)
        self.graph.remove_node(self.begin_node)
        self.graph.remove_node(self.end_node)
        self.begin_node = StartPointer()
        self.end_node = StopPointer()
        self.construction_pointer = self.begin_node
        self.graph.add_edge(self.construction_pointer, self.end_node)

#p = PlanFragment()
#p.add_action(SayInteractionAction('Hello!'))
#p.add_sequence([SayInteractionAction('How Are you?'), 
#                SayInteractionAction('Where are you from?')])
                                      
#p = PlanFragment() + SayInteractionAction('Hello!')
#condition = ValueOfVariable('@ctx.variables.my_boolean_variable')
#then_fragment = PlanFragment()+SayInteractionAction('Then Branch')
#else_fragment = PlanFragment()+SayInteractionAction('Else Branch')
#p.add_condition(condition,then_fragment,else_fragment)
#visualizer.show(p)

#block = PlanFragment()+SayInteractionAction('Hello!')
#p = PlanFragment().add_for_each('#ctx.variables.item','@ctx.variables.list',block)
#visualizer.show(p)

#block = PlanFragment()+SayInteractionAction('Hello!')
#p = PlanFragment().add_while(ValueOfVariable('@ctx.variables.my_boolean_variable'),block)
#visualizer.show(p)

